from sqlalchemy import create_engine, Column, String, Integer, and_
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
import cx_Oracle  # Oracle 驱动，需提前安装：pip install cx_Oracle
import logging

# -------------------------- 基础配置 --------------------------
# 日志配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# Oracle 数据库连接配置（源库 & 目标库，根据实际环境修改）
# 格式：oracle+cx_oracle://用户名:密码@IP:端口/服务名
SOURCE_DB_URL = "oracle+cx_oracle://user:password@192.168.1.100:1521/orcl"
TARGET_DB_URL = "oracle+cx_oracle://user:password@192.168.1.101:1521/orcl"

# 创建引擎（源库 & 目标库）
source_engine = create_engine(SOURCE_DB_URL)
target_engine = create_engine(TARGET_DB_URL)

# Session 工厂
SourceSession = sessionmaker(bind=source_engine)
source_session = SourceSession()

TargetSession = sessionmaker(bind=target_engine)
target_session = TargetSession()

# 基类
Base = declarative_base()

# -------------------------- 表模型定义 --------------------------
# 源表模型（TMS_DW_CURRENT_STATE）
class TmsDwCurrentState(Base):
    __tablename__ = 'TMS_DW_CURRENT_STATE'
    # 假设主键为 ID，实际请根据表结构调整！
    id = Column(Integer, primary_key=True)  
    module_name = Column(String(50))
    state = Column(String(50))
    # 其他字段按需补充...

# 目标表模型（需与源表同步结构，根据实际表名调整）
class TargetTable(Base):
    __tablename__ = 'TARGET_TABLE'  # 目标表名，根据需求修改
    id = Column(Integer, primary_key=True)  
    module_name = Column(String(50))
    state = Column(String(50))
    # 其他字段按需补充...

# -------------------------- 核心方法定义 --------------------------
def fetch_data():
    """
    从 Oracle 源库抓取数据：
    条件：module_name = 'PHOTO' + state in ('0010','4110','3100')
    """
    try:
        data = source_session.query(TmsDwCurrentState).filter(
            and_(
                TmsDwCurrentState.module_name == 'PHOTO',
                TmsDwCurrentState.state.in_(['0010', '4110', '3100'])
            )
        ).all()
        logging.info(f"成功抓取 {len(data)} 条数据")
        return data
    except Exception as e:
        logging.error(f"抓取数据失败: {str(e)}")
        return []

def batch_insert(data_list):
    """
    批量插入新增数据：
    逻辑：目标库不存在则插入
    """
    try:
        # 先查询目标库已存在的 ID，避免重复插入
        existing_ids = [
            obj.id for obj in target_session.query(TargetTable.id).all()
        ]
        # 筛选新增数据（源有 & 目标无）
        new_data = [
            obj for obj in data_list 
            if obj.id not in existing_ids
        ]
        # 批量插入
        if new_data:
            target_session.add_all(new_data)
            logging.info(f"准备插入 {len(new_data)} 条新增数据")
        else:
            logging.info("无新增数据需插入")
        return len(new_data)
    except Exception as e:
        logging.error(f"插入数据失败: {str(e)}")
        # 抛出异常，让上层事务回滚
        raise 

def batch_update(data_list):
    """
    批量更新数据：
    逻辑：源数据与目标库存在差异则更新
    """
    try:
        update_count = 0
        for source_obj in data_list:
            # 查询目标库对应记录
            target_obj = target_session.query(TargetTable).filter(
                TargetTable.id == source_obj.id
            ).first()
            if target_obj:
                # 对比字段（示例仅对比 state，需根据实际字段扩展）
                if target_obj.state != source_obj.state:
                    target_obj.state = source_obj.state
                    # 其他字段对比更新...
                    update_count += 1
        logging.info(f"准备更新 {update_count} 条变更数据")
        return update_count
    except Exception as e:
        logging.error(f"更新数据失败: {str(e)}")
        # 抛出异常，让上层事务回滚
        raise 

def batch_delete():
    """
    批量删除数据：
    逻辑：目标库存在 & 源库不存在 → 删除（需根据实际业务调整！）
    注意：实际场景可能需要用“逻辑删除标记”，物理删除需谨慎！
    """
    try:
        # 源库存在的 ID
        source_ids = [
            obj.id for obj in source_session.query(TmsDwCurrentState.id).all()
        ]
        # 目标库需删除的记录（源无 & 目标有）
        delete_objs = target_session.query(TargetTable).filter(
            TargetTable.id.notin_(source_ids)
        ).all()
        # 批量删除
        for obj in delete_objs:
            target_session.delete(obj)
        logging.info(f"准备删除 {len(delete_objs)} 条冗余数据")
        return len(delete_objs)
    except Exception as e:
        logging.error(f"删除数据失败: {str(e)}")
        # 抛出异常，让上层事务回滚
        raise 

# -------------------------- 事务控制主流程 --------------------------
def sync_data():
    """
    统一事务控制：
    流程：抓取 → 插入 → 更新 → 删除
    异常：任意步骤失败则整体回滚
    """
    try:
        # 1. 抓取数据
        data = fetch_data()
        if not data:
            logging.warning("无数据可同步，结束流程")
            return

        # 2. 开启事务（由 session 自动管理）
        # 3. 批量插入
        insert_count = batch_insert(data)
        # 4. 批量更新
        update_count = batch_update(data)
        # 5. 批量删除
        delete_count = batch_delete()

        # 6. 提交事务（所有操作成功才提交）
        target_session.commit()
        logging.info(f"""
            同步完成！
            - 新增: {insert_count} 条
            - 更新: {update_count} 条
            - 删除: {delete_count} 条
        """)
    except Exception as e:
        # 7. 回滚事务（任意步骤失败则回滚）
        target_session.rollback()
        logging.error(f"同步失败，事务已回滚: {str(e)}")
    finally:
        # 8. 关闭会话
        source_session.close()
        target_session.close()

# -------------------------- 执行入口 --------------------------
if __name__ == "__main__":
    sync_data()